package net.moonbiter.ebs.protocols.httpjava.client;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import net.moonbiter.BugException;
import net.moonbiter.ebs.ServiceDef;
import net.moonbiter.ebs.ServiceProviderDef;

import org.apache.log4j.Logger;

public class ServiceClientGenerator {
	
	private static final Logger logger = Logger.getLogger(ServiceClientGenerator.class);
	private ClassPool pool;
	
	public ServiceClientGenerator() {
		pool = ClassPool.getDefault();
	}
	
	public ServiceClientGenerator(ClassPool pool) {
		this.pool = pool;
	}
	
	public void addClassPathEntry(String path) throws NotFoundException{
		pool.appendClassPath(path);
	}
	
	private String generateMethodSignature(String serviceName, ServiceDef service){
		StringBuffer buf = new StringBuffer();
		buf.append(service.getReturnType().getCanonicalName()+ " ");
		logger.debug("added return type (buffer:"+buf.toString()+")");
		buf.append(serviceName+ "(");
		logger.debug("added service name (buffer:"+buf.toString()+")");
		boolean firstParam = true;
		for (String paramName : service.getOrderedInputNames()){
			if (firstParam){				
				firstParam = false;
			} else {
				buf.append(", ");
			}
			Class<?> paramType = service.getInputDef(paramName).getType();
			logger.debug("param type: "+paramType);
			String paramDecl = paramType.getCanonicalName()+" "+paramName; 
			logger.debug("declaring param: "+paramDecl);
			buf.append(paramDecl);
		}		
		buf.append(")");
		if (service.getDeclaredExceptions().length>0){
			buf.append(" throws ");
			boolean firstEx = true;
			for (Class<?> excep : service.getDeclaredExceptions()){
				if (firstEx){				
					firstEx = false;
				} else {
					buf.append(", ");
				}
				buf.append(excep.getCanonicalName());
			}
		}
		String result = buf.toString();
		return result; 
	}
	
	private String generateInterfaceMethod(String serviceName, ServiceDef service){
		logger.debug("generating interface's method for service \""+serviceName+"\"");
		StringBuffer buf = new StringBuffer();
		buf.append(generateMethodSignature(serviceName, service));
		buf.append(";");
		String result = buf.toString();
		logger.debug("result produced:"+result);
		return result; 
	}
	
	private String generateClassMethod(String serviceName, ServiceDef service){
		logger.debug("generating interface's method for service \""+serviceName+"\"");
		StringBuffer buf = new StringBuffer();
		buf.append(generateMethodSignature(serviceName, service));
		buf.append("{");
		buf.append("}");
		String result = buf.toString();
		logger.debug("result produced:"+result);
		return result; 
	}	

	public CtClass generateInterface(String packageName, String providerName, ServiceProviderDef provider) {		
		String canonicalName = (packageName.equals("")?providerName:packageName+"."+providerName);
		CtClass provInterface = pool.makeInterface(canonicalName);
		
		for (String servName : provider.getServicesNames()){
			ServiceDef serv = provider.getService(servName);
			
			CtMethod servMethod;
			try {
				servMethod = CtMethod.make(generateInterfaceMethod(servName, serv), provInterface);
				provInterface.addMethod(servMethod);
			} catch (CannotCompileException e) {
				throw new BugException("I generated bad code",e);
			}			
		}		
				
		return provInterface;
	}
	
	public CtClass generateClass(String packageName, String providerName, ServiceProviderDef provider) {		
		String canonicalName = (packageName.equals("")?providerName:packageName+"."+providerName);
		CtClass provClassServ = pool.makeClass(canonicalName);
		
		for (String servName : provider.getServicesNames()){
			ServiceDef serv = provider.getService(servName);
			
			CtMethod servMethod;
			try {
				servMethod = CtMethod.make(generateClassMethod(servName, serv), provClassServ);
				provClassServ.addMethod(servMethod);
			} catch (CannotCompileException e) {
				throw new BugException("I generated bad code",e);
			}			
		}		
				
		return provClassServ;
	}	
}
